package com.github.chirspan.xaas.core.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.dom4j.*;
import org.dom4j.io.SAXReader;

import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Iterator;

/**
 * *******description*******
 * <p>
 * *************************
 *
 * @author chenpan
 * @date 2019/11/6 10:46
 */
public class XmlDomUtils {

    public static enum NodeType {
        Comment, Element
    }

    public static int id = 1;
    public static HashMap<String, Integer> tags;
    public static Namespace qNs;

    public static JSONObject genStructure(String xml) throws DocumentException {
        return genStructure(xml, null, null);
    }

    /**
     * 遍历xml获取xml结构json
     *
     * @param xml
     * @return
     */
    public static JSONObject genStructure(String xml, String ns, String nsPrefix) throws DocumentException {

        SAXReader saxReader = new SAXReader();
        if (StrUtil.isNotBlank(ns)) {
            qNs = new Namespace(nsPrefix, ns);
        }
        JSONObject xmlJson = new JSONObject();
        id = 1;
        tags = new HashMap<>();
        Document document = saxReader.read(new ByteArrayInputStream(xml.getBytes()));
        Element root = document.getRootElement();
        root.setQName(new QName(root.getName(), qNs));
        getChildNodes(root, xmlJson);
        return xmlJson;
    }

    protected static void getChildNodes(Element root, JSONObject xmlJson) {
        //当前节点的名称、文本内容和属性
        xmlJson.put("nodeId", id++);
        xmlJson.put("nodeKey", getTagName(root.getName()));
        xmlJson.put("nodeTag", root.getName());
        xmlJson.put("path", root.getPath());
        xmlJson.put("type", root.getNodeTypeName());
        xmlJson.put("nodeValue", root.getTextTrim());
        xmlJson.put("parentNodeId", "");

        Iterator<Attribute> attrIt = root.attributeIterator();
        JSONArray array = new JSONArray();
        while (attrIt.hasNext()) {
            JSONObject obj = new JSONObject();
            Attribute attribute = attrIt.next();
            obj.put("nodeId", id++);
            obj.put("nodeKey", getTagName(attribute.getName()));
            obj.put("nodeTag", attribute.getName());
            obj.put("path", attribute.getPath());
            obj.put("type", attribute.getNodeTypeName());
            obj.put("nodeValue", attribute.getValue());
            obj.put("parentNodeId", xmlJson.get("nodeId"));
            array.add(obj);
        }


        //子节点迭代
        Iterator<Node> it = root.nodeIterator();
        while (it.hasNext()) {
            Node node = it.next();
            if (node instanceof Element) {
                Element elem = (Element) node;
                elem.setQName(new QName(elem.getName(), qNs));
                JSONObject json = new JSONObject();
                getChildNodes(elem, json);
                json.put("parentNodeId", xmlJson.get("nodeId"));
                array.add(json);
            } else if (node.getNodeType() != Node.TEXT_NODE) {
                JSONObject obj = new JSONObject();
                if (node.getNodeType() == Node.COMMENT_NODE) {
                    obj.put("nodeId", id++);
                    obj.put("nodeKey", getTagName(node.getText()));
                    obj.put("nodeTag", node.getText());
                    //obj.put("xpath", node.getPath());
                    obj.put("path", "");
                    obj.put("type", node.getNodeTypeName());
                    obj.put("nodeValue", "");
                } else {
                    obj.put("nodeId", id++);
                    obj.put("nodeKey", getTagName(node.getName()));
                    obj.put("nodeTag", node.getName());
                    obj.put("path", node.getPath());
                    obj.put("type", node.getNodeTypeName());
                    obj.put("nodeValue", node.getText());
                }
                obj.put("parentNodeId", xmlJson.get("nodeId"));
                array.add(obj);
            }
        }

        if (CollectionUtil.isNotEmpty(array)) {
            xmlJson.put("children", array);
        }

    }

    private static String getTagName(String tag) {
        if (StrUtil.isBlank(tag)) {
            return null;
        }
        String tagName;
        if (tags.get(tag) == null || tags.get(tag) == 0) {
            tagName = tag;
            tags.put(tag, 1);
        } else {
            tagName = tag + "_" + tags.get(tag);
            tags.put(tag, tags.get(tag) + 1);
        }

        return tagName;
    }

    public static Document loadDocument(String xmlStr)
            throws DocumentException, UnsupportedEncodingException {
        SAXReader saxReader = new SAXReader();
        return saxReader.read(new ByteArrayInputStream(xmlStr.getBytes(StandardCharsets.UTF_8)));
    }

    public static void main(String[] args) {
        String xml = "<MCCI_IN000002UV01 ITSVersion=\"XML_1.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"urn:hl7-org:v3\" xsi:schemaLocation=\"urn:hl7-org:v3 ../multicacheschemas/MCCI_IN000002UV01.xsd\">\n" +
                "\t<id root=\"2.16.156.10011.2.5.1.1\" extension=\"@12122\"/>\n" +
                "\t<creationTime value=\"20170106151903\"/>\n" +
                "\t<interactionId root=\"2.16.156.10011.2.5.1.2\" extension=\"MCCI_IN000002UV01\"/>\n" +
                "\t<processingCode code=\"P\"/>\n" +
                "\t<processingModeCode/>\n" +
                "\t<acceptAckCode code=\"AL\"/>\n" +
                "\t<receiver typeCode=\"RCV\">\n" +
                "\t\t<device classCode=\"DEV\" determinerCode=\"INSTANCE\">\n" +
                "\t\t\t<id>\n" +
                "\t\t\t\t<item root=\"2.16.156.10011.2.5.1.3\" extension=\"@111\"/>\n" +
                "\t\t\t</id>\n" +
                "\t\t</device>\n" +
                "\t</receiver>\n" +
                "\t<sender typeCode=\"SND\">\n" +
                "\t\t<device classCode=\"DEV\" determinerCode=\"INSTANCE\">\n" +
                "\t\t\t<id>\n" +
                "\t\t\t\t<item root=\"2.16.156.10011.2.5.1.3\" extension=\"@222\"/>\n" +
                "\t\t\t</id>\n" +
                "\t\t</device>\n" +
                "\t</sender>\n" +
                "\t<acknowledgement typeCode=\"AE\">\n" +
                "\t\t<!--请求消息ID-->\n" +
                "\t\t<targetMessage>\n" +
                "\t\t\t<id root=\"2.16.156.10011.2.5.1.1\" extension=\"请求的消息ID\"/>\n" +
                "\t\t</targetMessage>\n" +
                "\t\t<acknowledgementDetail>\n" +
                "\t\t\t<text value=\"处理结果说明\"/>\n" +
                "\t\t</acknowledgementDetail>\n" +
                "\t</acknowledgement>\n" +
                "</MCCI_IN000002UV01>";
        try {
            System.out.println(genStructure(xml, "urn:hl7-org:v3", "s"));
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }
}
